Amazon Rekognitionでイメージの顔検出による感情分析を行う
こんにちは、CX事業本部 IoT事業部の若槻です。
Amazon Rekognitionは、機械学習を利用した画像や動画の分析を簡単に実施できるAWSのサービスです。
今回は、Amazon Rekognitionでイメージの顔検出による感情分析を行ってみました。
顔検出による感情分析
Amazon Rekognitionで感情分析を行う場合は顔検出APIのDetectFacesを使用します。
DetectFacesのリクエストの構文は下記のようになります。
Image
パラメータには、Bytes
で分析対象の画像データのバイトデータを直接指定、またはS3Object
で画像データのS3オブジェクトを指定します。Attributes
パラメータには、レスポンスデータに含みたい属性を指定します。既定では感情の属性はレスポンスに含まれませんが、ALL
を指定することにより含めることが出来ます。
{ "Image": { "Bytes": blob, "S3Object": { "Bucket": "string", "Name": "string", "Version": "string" } }, "Attributes": [ "string" ] }
DetectFacesのレスポンスのサンプルは下記のようになります(感情分析属性部分のみ抜き出しています)。Emotions
属性に各タイプの感情データがリストで格納されています。
{ "FaceDetails": [ { "Emotions": [ { "Type": "HAPPY", "Confidence": 97.60749053955078 }, { "Type": "SURPRISED", "Confidence": 0.7658454179763794 }, { "Type": "CONFUSED", "Confidence": 0.5557805299758911 }, { "Type": "ANGRY", "Confidence": 0.3515094518661499 }, { "Type": "DISGUSTED", "Confidence": 0.34102049469947815 }, { "Type": "CALM", "Confidence": 0.19356855750083923 }, { "Type": "FEAR", "Confidence": 0.13799776136875153 }, { "Type": "SAD", "Confidence": 0.046782251447439194 } ] } ] }
検出できる感情タイプ
DetectFacesで検出できる感情は下記の8タイプとなります。
HAPPY
:幸せSURPRISED
:驚きCONFUSED
:困惑ANGRY
:怒りDISGUSTED
:うんざりCALM
:穏やかFEAR
:恐れSAD
:悲しみ-
aws-sdk-js-v3/models_0.ts at 44635811e48c77dbdad388eb75ca92cdc0882c98 · aws/aws-sdk-js-v3
感情分析結果から感情を推定
レスポンスのサンプルにあったように、感情分析の結果はどれか一つの感情タイプが検出されるのでなく、8種類の感情タイプそれぞれに対する信頼度(Confidential)が検出されたデータとなります。よって一つの感情タイプを推定したい場合は、感情検出結果のリストから信頼度の一番高い感情を1つ取得する必要があります。
下記は感情を推定するサンプルコード(TypeScript)です。感情分析結果のリストからConfidence
が最大のデータの感情を取得し、推定結果としています。
import Rekognition, { FaceDetailList, Emotions, Emotion, DetectFacesRequest, } from 'aws-sdk/clients/rekognition'; /** * 顔検出での感情推定結果を取得する * @param imageData 画像データ(Base64) */ export const getEmotion = async (imageData: string): Promise<void> => { const rekognitionClient = new Rekognition({ apiVersion: '2016-06-27', }); const params: DetectFacesRequest = { Image: { Bytes: Buffer.from( imageData.replace('data:image/jpeg;base64,', ''), 'base64', ), }, Attributes: ['ALL'], }; const rawResult = await rekognitionClient.detectFaces(params).promise(); if (rawResult.FaceDetails?.length === 0) { console.log('顔は検出されませんでした。'); } else { //感情検出結果のリストから信頼度の一番高い感情データを1つ取得 const emotions = (rawResult.FaceDetails as FaceDetailList)[0] .Emotions as Emotions; const largestEmotionConfidence = Math.max( ...(emotions.map((d) => d.Confidence) as number[]), ); const emotion = emotions.find( (d) => d.Confidence === largestEmotionConfidence, ) as Emotion; console.log(emotion); } };
React(react-webcam)で感情推定してみる
先日投稿したエントリのReactアプリに感情推定機能を実装してみました。ハイライト部分が追記箇所です。
import { useRef, useState, useCallback } from "react"; import Webcam from "react-webcam"; import { makeStyles } from "@material-ui/core/styles"; import { DetectFacesRequest, DetectFacesResponse, FaceDetailList, Emotions, Emotion, } from "aws-sdk/clients/rekognition"; import AWS from "aws-sdk"; const useStyles = makeStyles(() => ({ webcam: { position: "absolute", top: "0px", left: "0px", visibility: "hidden", }, rekognizeResult: { flex: 1, width: "100%", flexDirection: "row", justifyContent: "flex-start", }, })); const videoConstraints = { width: 720, height: 360, facingMode: "user", }; AWS.config.update({ accessKeyId: process.env.REACT_APP_AWS_ACCESS_KEY_ID, secretAccessKey: process.env.REACT_APP_AWS_SECRET_ACCESS_KEY, region: process.env.REACT_APP_AWS_REGION, }); const rekognitionClient = new AWS.Rekognition({ apiVersion: "2016-06-27", }); //Amazon Rekognitionによる顔分析 const detectFaces = async (imageData: string): Promise<DetectFacesResponse> => { const params: DetectFacesRequest = { Image: { Bytes: Buffer.from( imageData.replace("data:image/jpeg;base64,", ""), "base64" ), }, Attributes: ["ALL"], }; return await rekognitionClient.detectFaces(params).promise(); }; //分析結果からConfidence(分析結果の信頼度)取得 const getConfidence = (rekognizeResult: DetectFacesResponse): number => { return (rekognizeResult.FaceDetails as FaceDetailList)[0].Confidence!; }; //分析結果からLowAge(推測される年齢範囲の加減)取得 const getLowAge = (rekognizeResult: DetectFacesResponse): number => { return (rekognizeResult.FaceDetails as FaceDetailList)[0].AgeRange?.Low!; }; //分析結果からHighAge(推測される年齢範囲の上限)取得 const getHighAge = (rekognizeResult: DetectFacesResponse): number => { return (rekognizeResult.FaceDetails as FaceDetailList)[0].AgeRange?.High!; }; //分析結果からEyeglasses(眼鏡を掛けているか)取得 const getIsWearingEyeGlasses = ( rekognizeResult: DetectFacesResponse ): boolean => { return (rekognizeResult.FaceDetails as FaceDetailList)[0].Eyeglasses?.Value!; }; //分析結果からEyeglasses(サングラスを掛けているか)取得 const getIsWearingSunGlasses = ( rekognizeResult: DetectFacesResponse ): boolean => { return (rekognizeResult.FaceDetails as FaceDetailList)[0].Sunglasses?.Value!; }; //分析結果から感情を推定 const getEmotion = (rekognizeResult: DetectFacesResponse): string => { const emotions = (rekognizeResult.FaceDetails as FaceDetailList)[0] .Emotions as Emotions; const largestEmotionConfidence = Math.max( ...(emotions.map((d) => d.Confidence) as number[]) ); const emotion = emotions.find( (d) => d.Confidence === largestEmotionConfidence ) as Emotion; return emotion.Type as string; }; const App = () => { const classes = useStyles(); const [isCaptureEnable, setCaptureEnable] = useState<boolean>(false); const webcamRef = useRef<Webcam>(null); const [url, setUrl] = useState<string | null>(null); const capture = useCallback(() => { const imageSrc = webcamRef.current?.getScreenshot(); if (imageSrc) { setUrl(imageSrc); setRekognizeResult(undefined); } }, [webcamRef]); const [rekognizeResult, setRekognizeResult] = useState<DetectFacesResponse>(); const rekognizeHandler = async () => { const result: DetectFacesResponse = await detectFaces(url as string); setRekognizeResult(result); console.log(result); }; return ( <> <header> <h1>カメラアプリ(顔分析付き)</h1> </header> {isCaptureEnable || ( <button onClick={() => setCaptureEnable(true)}>開始</button> )} {isCaptureEnable && ( <> <div> <button onClick={() => setCaptureEnable(false)}>終了</button> </div> <div> <Webcam audio={false} width={540} height={360} ref={webcamRef} screenshotFormat="image/jpeg" videoConstraints={videoConstraints} className={classes.webcam} /> </div> <button onClick={capture}>キャプチャ</button> </> )} {url && ( <> <div> <button onClick={() => { setUrl(null); setRekognizeResult(undefined); }} > 削除 </button> <button onClick={() => rekognizeHandler()}>分析</button> </div> <div> <img src={url} alt="Screenshot" /> </div> {typeof rekognizeResult !== "undefined" && ( <div className={classes.rekognizeResult}> <div>{"Confidence: " + getConfidence(rekognizeResult)}</div> <div> {"AgeRange: " + getLowAge(rekognizeResult) + " ~ " + getHighAge(rekognizeResult)} </div> <div> {"Eyeglasses: " + getIsWearingEyeGlasses(rekognizeResult)} </div> <div> {"Sunglasses: " + getIsWearingSunGlasses(rekognizeResult)} </div> <div>{"Emotion: " + getEmotion(rekognizeResult)}</div> </div> )} </> )} </> ); }; export default App;
- CALM(穏やか)
-
SURPRISED(驚き)
-
HAPPY(幸せ)
-
SAD(悲しみ)
-
CONFUSED(混乱)
狙った感情を分析結果で出すのがなかなか難しくてゲームみたいになりました。
参考
以上